﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SysMisc
{
    /// <summary>
    /// ByteBuffer作为一个接收数据的缓冲区，在接收到完整的一帧数据数据之后，
    /// 最好在读取完相应的一帧数据之后，调用DiscardReadedBytes();这个方法的作用是，将未读取的缓冲区数据，重新移到缓冲区的起始。
    /// </summary>
    public class ByteBuffer
    {
        //字节缓存区
        private byte[] _buf;
        //读取索引
        private int readIndex = 0;
        //写入索引
        private int writeIndex = 0;
        //读取索引标记
        private int markReadIndex = -1;
        //写入索引标记
        private int markWirteIndex = -1;
        //缓存区字节数组的长度
        private int capacity;

        /**
         * 构造方法
         */
        private ByteBuffer(int capacity)
        {
            _buf = new byte[capacity];
            this.capacity = capacity;
        }

        /**
         * 构造方法
         */
        private ByteBuffer(byte[] bytes)
        {
            _buf = bytes;
            this.capacity = bytes.Length;
        }

        /**
         * 构建一个capacity长度的字节缓存区ByteBuffer对象
         */
        public static ByteBuffer Allocate(int capacity)
        {
            return new ByteBuffer(capacity);
        }

        /**
         * 构建一个以bytes为字节缓存区的ByteBuffer对象，一般不推荐使用
         */
        public static ByteBuffer Allocate(byte[] bytes)
        {
            return new ByteBuffer(bytes);
        }

        /**
         * 确定内部字节缓存数组的大小
         */
        private int FixSizeAndReset(int currLen, int futureLen)
        {
            if (futureLen > currLen)
            {
                //以原大小的2次方数的两倍确定内部字节缓存区大小
                int size = FixLength(currLen) * 2;
                if (futureLen > size)
                {
                    //以将来的大小的2次方的两倍确定内部字节缓存区大小
                    size = FixLength(futureLen) * 2;
                }
                byte[] newbuf = new byte[size];
                Array.Copy(_buf, 0, newbuf, 0, currLen);
                _buf = newbuf;
                capacity = newbuf.Length;
            }
            return futureLen;
        }

        /// <summary>
        /// 将bytes字节数组从startIndex开始的length字节写入到此缓存区,并返回写入的数据长度，写入失败，则返回一个负数
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public int WriteBytes(byte[] bytes, int startIndex, int length)
        {
            if (bytes.Length < startIndex + length)
                return -1;
            lock (this)
            {
                int seek_max = writeIndex + length;
                if( seek_max > _buf.Length)
                {/*如果写入全部数据，则当前索引会超出*/
                    if (length + (writeIndex - readIndex) < _buf.Length)
                    {/*如果当前写入的数据，加上缓冲区已有的数据，没有超出最大长度，则先将全部数据进行前移*/
                        moveBufferToSeekZero();
                    }
                    else {/*否则的话，数据太长，放不下，则暂时直接报错*/
                        //int total = offset + writeIndex;
                        //int len = buf.Length;
                        //FixSizeAndReset(len, total);
                        return -1;
                    }
                }
                Buffer.BlockCopy(bytes, startIndex, _buf, writeIndex, length);  /*拷贝入数据*/
                writeIndex += length;/*更新指针*/
                return length;
            }
        }

        /**
         * 将字节数组中从0到length的元素写入缓存区
         */
        public int WriteBytes(byte[] bytes, int length)
        {
            return WriteBytes(bytes, 0, length);
        }

        /**
         * 将字节数组全部写入缓存区
         */
        public int WriteBytes(byte[] bytes)
        {
            return WriteBytes(bytes, bytes.Length);
        }

        /**
         * 将一个ByteBuffer的有效字节区写入此缓存区中
         */
        public void Write(ByteBuffer buffer)
        {
            if (buffer == null) return;
            if (buffer.ReadableBytes() <= 0) return;
            WriteBytes(buffer.ToArray());
        }

        /**
         * 写入一个int16数据
         */
        public void WriteShort(short value)
        {
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 写入一个uint16数据
         */
        public void WriteUshort(ushort value)
        {
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 写入一个int32数据
         */
        public void WriteInt(int value)
        {
            //byte[] array = new byte[4];
            //for (int i = 3; i >= 0; i--)
            //{
            //    array[i] = (byte)(value & 0xff);
            //    value = value >> 8;
            //}
            //Array.Reverse(array);
            //Write(array);
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 写入一个uint32数据
         */
        public void WriteUint(uint value)
        {
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 写入一个int64数据
         */
        public void WriteLong(long value)
        {
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 写入一个uint64数据
         */
        public void WriteUlong(ulong value)
        {
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 写入一个float数据
         */
        public void WriteFloat(float value)
        {
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 写入一个byte数据
         */
        public void WriteByte(byte value)
        {
            lock (this)
            {
                int afterLen = writeIndex + 1;
                int len = _buf.Length;
                FixSizeAndReset(len, afterLen);
                _buf[writeIndex] = value;
                writeIndex = afterLen;
            }
        }

        /**
         * 写入一个double类型数据
         */
        public void WriteDouble(double value)
        {
            WriteBytes(flip(BitConverter.GetBytes(value)));
        }

        /**
         * 读取一个字节
         */
        public byte ReadByte()
        {
            byte b = _buf[readIndex];
            readIndex++;
            return b;
        }

        /// <summary>
        /// 从读取索引位置开始读取len长度的字节数组,如果长度大于可取数组，则返回 null
        /// </summary>
        /// <param name="len"></param>
        /// <returns></returns>
        private byte[] Read(int len)
        {
            if( len <= (writeIndex - readIndex)) {
                byte[] bytes = new byte[len];
                Array.Copy(_buf, readIndex, bytes, 0, len);
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(bytes);
                }
                readIndex += len;
                return bytes;
            }
            else { return null; }
        }

        /**
         * 读取一个uint16数据
         */
        public ushort ReadUshort()
        {
            return BitConverter.ToUInt16(Read(2), 0);
        }

        /**
         * 读取一个int16数据
         */
        public short ReadShort()
        {
            return BitConverter.ToInt16(Read(2), 0);
        }

        /**
         * 读取一个uint32数据
         */
        public uint ReadUint()
        {
            return BitConverter.ToUInt32(Read(4), 0);
        }

        /**
         * 读取一个int32数据
         */
        public int ReadInt()
        {
            return BitConverter.ToInt32(Read(4), 0);
        }

        /**
         * 读取一个uint64数据
         */
        public ulong ReadUlong()
        {
            return BitConverter.ToUInt64(Read(8), 0);
        }

        /**
         * 读取一个long数据
         */
        public long ReadLong()
        {
            return BitConverter.ToInt64(Read(8), 0);
        }

        /**
         * 读取一个float数据
         */
        public float ReadFloat()
        {
            return BitConverter.ToSingle(Read(4), 0);
        }

        /**
         * 读取一个double数据
         */
        public double ReadDouble()
        {
            return BitConverter.ToDouble(Read(8), 0);
        }

        /**
         * 从读取索引位置开始读取len长度的字节到disbytes目标字节数组中
         * @params disstart 目标字节数组的写入索引
         */
        public void ReadBytes(byte[] disbytes, int disstart, int len)
        {
            int size = disstart + len;
            for (int i = disstart; i < size; i++)
            {
                disbytes[i] = this.ReadByte();
            }
        }

        /**
         * 清除已读字节并重建缓存区，通常是在从缓冲区中完整读取玩一帧数据之后调用，用来整理缓冲区内的数据，已进行下一次处理
         */
        public void DiscardReadedBytes()
        {
            int pos_start = readIndex;
            int pos_end = writeIndex;
            int carry_len = pos_end - pos_start;
            if (carry_len > 0)
            {
                byte[] buf_tmp = new byte[carry_len];
                Buffer.BlockCopy(_buf, pos_start, buf_tmp, 0, carry_len);
                Buffer.BlockCopy(buf_tmp, 0, _buf, 0, carry_len);
            }
            readIndex = 0;
            writeIndex = carry_len;
            markReadIndex = -1;
            markWirteIndex = -1;
        }

        /**
         * 清空此对象
         */
        public void Clear()
        {
            readIndex = 0;
            writeIndex = 0;
            markReadIndex = -1;
            markWirteIndex = -1;
        }

        /**
         * 设置开始读取的索引
         */
        public Boolean SetReaderIndex(int index)
        {
            if ((index < 0) || (index >= writeIndex))
                return false;
            else
            {
                readIndex = index;
                return true;
            }
        }

        /**
         * 标记读取的索引位置
         */
        public void MarkReaderIndex()
        {
            markReadIndex = readIndex;
        }

        /**
         * 标记写入的索引位置
         */
        public void MarkWriterIndex()
        {
            markWirteIndex = writeIndex;
        }

        /**
         * 将读取的索引位置重置为标记的读取索引位置
         */
        public void ResetReaderIndex()
        {
            readIndex = markReadIndex;
        }

        /**
         * 将写入的索引位置重置为标记的写入索引位置
         */
        public void ResetWriterIndex()
        {
            writeIndex = markWirteIndex;
        }
        /// <summary>
        /// 可写的有效字节数
        /// </summary>
        /// <returns></returns>
        public int WriteAbleBytes()
        {
            return _buf.Length - (writeIndex - readIndex);
        }

        /// <summary>
        /// 可读的有效字节数
        /// </summary>
        /// <returns></returns>
        public int ReadableBytes()
        {
            return writeIndex - readIndex;
        }

        /// <summary>
        /// 获取可读的字节数组,与ReadableBytes()的区别是，此方法不更新readIndex参数，而直接返回全部可操作的数组
        /// </summary>
        /// <returns></returns>
        public byte[] ToArray()
        {
            //byte[] bytes = new byte[writeIndex];
            byte[] bytes = new byte[writeIndex - readIndex]; /*此处，我将writeIndex -> writeIndex - readIndex*/
            //Array.Copy(buf, 0, bytes, 0, bytes.Length);
            Array.Copy(_buf, readIndex, bytes, 0, bytes.Length);
            return bytes;
        }

        /**
         * 获取缓存区大小
         */
        public int GetCapacity()
        {
            return this.capacity;
        }

        /*************************************************************************以下为 私有方法*/

        /**
         * 根据length长度，确定大于此leng的最近的2次方数，如length=7，则返回值为8
         */
        private int FixLength(int length)
        {
            int n = 2;
            int b = 2;
            while (b < length)
            {
                b = 2 << n;
                n++;
            }
            return b;
        }

        /**
         * 翻转字节数组，如果本地字节序列为低字节序列，则进行翻转以转换为高字节序列
         */
        private byte[] flip(byte[] bytes)
        {
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }
            return bytes;
        }

        /// <summary>
        /// 清除已读数据，并重置缓冲区标识，最后决定并不把markReadIndex作为移动数据的参考量，不然markReadIndex必须要保证不用的时候是-1
        /// 不然因为markReadIndex并不随着readIndex移动，导致数据移动会出现问题，暂时功能和DiscardReadedBytes()方法相同。
        /// </summary>
        private void moveBufferToSeekZero()
        {
            int pos_start = readIndex;
            int pos_end = writeIndex;
            int carry_len = pos_end - pos_start;
            if (carry_len > 0) {
                byte[] buf_tmp = new byte[carry_len];
                Buffer.BlockCopy(_buf, pos_start, buf_tmp, 0, carry_len);
                Buffer.BlockCopy(buf_tmp, 0, _buf, 0, carry_len);
            }
            readIndex = 0;
            writeIndex = -1;
            markReadIndex = carry_len;
            markWirteIndex = -1;
        }
        private void moveBufferToSeekZero_bak()
        {
            int pos_start = readIndex < markReadIndex ? readIndex : markReadIndex;
            int pos_end = writeIndex > markWirteIndex ? writeIndex : markWirteIndex;
            int carry_len = pos_end - pos_start;
            byte[] buf_tmp = new byte[carry_len];
            Buffer.BlockCopy(_buf, pos_start, buf_tmp, 0, carry_len);
            Buffer.BlockCopy(buf_tmp, 0, _buf, 0, carry_len);
            readIndex -= pos_start;
            writeIndex -= pos_start;
            markReadIndex -= pos_start;
            markWirteIndex -= pos_start;
        }
    }
}
